from PyQt5.QtWidgets import QApplication, QMainWindow
import MainWindow
import datetime
import socket
import threading
import time
import traceback
from PyQt5.QtCore import Qt


def log(*text):
    text = str(datetime.datetime.now())[:19] + "--" + ", ".join([str(i) for i in text])
    print(text)
    return text + "\n\n"


def thread_it(func, *args):
    t = threading.Thread(target=func, args=args)
    t.setDaemon(True)
    t.start()


class my_socket:
    def __init__(self) -> None:
        self.send_queue = []

    def send(self, send_address, send_port, message):
        now = time.time()
        self.send_queue.append((send_address, send_port, message, now))

    def do_send(self, send_address, send_port, message):
        self.send_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.send_socket.connect((send_address, send_port))
        self.send_socket.send(str(message).encode())
        self.send_socket.close()

    def delete(self, del_index):
        del self.send_queue[del_index]

    def update_thread(self, delay=5):
        while True:
            try:
                now = time.time()
                delete_list = []
                for i in self.send_queue:
                    if now - i[3] > delay:
                        self.do_send(i[0], i[1], i[2])
                        delete_list.append(i)
                for i in delete_list:
                    self.send_queue.remove(i)
            except:
                print(traceback.format_exc())
                break

    def run(self):
        thread_it(self.update_thread)


delay_socket = my_socket()
delay_socket.run()


class send_queue:
    def __init__(self, seq_count, send_window_size):
        self.serial_number_to_be_sent = []
        self.have_send = set()  # 已经发送的序列号
        self.have_ack = set()
        self.send_time = dict()
        self.send_window = [0, send_window_size]
        for i in range(1, seq_count + 1):
            self.serial_number_to_be_sent.append(i)

    def get(self):
        # 返回下一个要发送的序列号
        if len(self.serial_number_to_be_sent) == 0:
            return None
        for i in range(self.send_window[0], self.send_window[1]):
            if self.serial_number_to_be_sent[i] not in self.have_send:
                return self.serial_number_to_be_sent[i]
        return None

    def do_send(self, seq_num, send_time):
        self.have_send.add(seq_num)
        self.send_time[seq_num] = send_time

    def update(self, now, timeout=15):
        # 删除超时的已发送
        delete_list = []
        for i in self.have_send:
            if now - self.send_time[i] > timeout:
                delete_list.append(i)
        for i in delete_list:
            self.have_send.remove(i)

    def ack(self, ack_num):
        self.have_ack.add(ack_num)
        # 更新发送窗口
        while self.serial_number_to_be_sent[self.send_window[0]] in self.have_ack:
            self.send_window[0] += 1
            self.send_window[1] = min(
                self.send_window[1] + 1, len(self.serial_number_to_be_sent)
            )
            if self.send_window[0] == len(self.serial_number_to_be_sent):
                break


class My_MainWindow(QMainWindow, MainWindow.Ui_MainWindow):
    def __init__(self, parent=None):
        super(My_MainWindow, self).__init__(parent)
        self.setupUi(self)

        self.packet_send_buttons = [self.pushButton, self.pushButton_2, self.pushButton_3, self.pushButton_4,
                                    self.pushButton_5, self.pushButton_12]
        self.packet_receive_buttons = [self.pushButton_6, self.pushButton_7, self.pushButton_8, self.pushButton_9,
                                       self.pushButton_10, self.pushButton_13]
        self.data_label_to_be_sent = [self.label, self.label_2, self.label_3, self.label_4, self.label_5,
                                      self.label_6, self.label_7, self.label_8, self.label_9, self.label_10,
                                      self.label_11, self.label_12, self.label_13, self.label_14, self.label_15]
        self.data_label_to_be_received = [self.label_16, self.label_17, self.label_18, self.label_19, self.label_20,
                                          self.label_21, self.label_22, self.label_23, self.label_24, self.label_25,
                                          self.label_26, self.label_27, self.label_28, self.label_29, self.label_30]

        # 发送者内部变量
        self.sender_address = '127.0.0.1'
        self.sender_send_port = 10000
        self.sender_receive_port = 10001
        self.sender_send_queue = send_queue(16, 5)

        # 接收者内部变量
        self.receiver_address = '127.0.0.1'
        self.receiver_send_port = 10001
        self.receiver_receive_port = 10000
        self.last_received_ack = None

        # 主程序内部变量
        self.send_occupied_buttons = set()
        self.send_occupied_buttons_lock = threading.Lock()
        self.receive_occupied_buttons = set()
        self.receive_occupied_buttons_lock = threading.Lock()
        self.send_button_to_packet = dict()

        self.init()

    def init(self):
        for i in self.packet_send_buttons:
            i.hide()
        for i in self.packet_receive_buttons:
            i.hide()
        # 设置data_label_to_be_sent的文字居中
        for i in self.data_label_to_be_sent:
            i.setAlignment(Qt.AlignCenter)
        # 设置data_label_to_be_received的文字居中
        for i in self.data_label_to_be_received:
            i.setAlignment(Qt.AlignCenter)
        # 设置data_label_to_be_sent的文字为0到14
        for i in range(15):
            self.data_label_to_be_sent[i].setText(str(i))
        # 设置data_label_to_be_received的文字为0到14
        for i in range(15):
            self.data_label_to_be_received[i].setText(str(i))
        # 把data_label_to_be_sent的前5个label背景设置为蓝色
        for i in range(5):
            self.data_label_to_be_sent[i].setStyleSheet("background-color: rgb(0, 170, 255);")
        for i in self.packet_send_buttons:
            i.clicked.connect(self.send_pushButton_clicked)
        for i in self.packet_receive_buttons:
            i.clicked.connect(self.receive_pushButton_clicked)
        self.pushButton_11.clicked.connect(self.start_simulation_pushButton_clicked)

    def get_available_button(self, buttons, occupied_buttons):
        for i in buttons:
            if i not in occupied_buttons:
                return i
        return None

    def update_button_position_thread(self):
        while True:
            try:
                now = time.time()
                # 发送按钮开始位置：(110, 60)，结束位置：(960, 60)。接收按钮开始位置：(960, 160)，结束位置：(110, 160)
                # 按钮在5s内从开始位置移动到结束位置
                with self.send_occupied_buttons_lock:
                    delete_list = []
                    for i in self.send_occupied_buttons:
                        if i.isHidden():
                            i.show()
                        message, send_time = self.send_button_to_packet[i]
                        # 计算按钮的位置
                        x = int(110 + (960 - 110) * (now - send_time) / 5)
                        if x >= 960:
                            # 如果按钮已经到达结束位置，隐藏按钮
                            i.hide()
                            delete_list.append(i)
                        else:
                            i.setGeometry(x, 60, 100, 50)
                    for i in delete_list:
                        self.send_occupied_buttons.remove(i)
                with self.receive_occupied_buttons_lock:
                    delete_list = []
                    for i in self.receive_occupied_buttons:
                        if i.isHidden():
                            i.show()
                        message, send_time = self.send_button_to_packet[i]
                        # 计算按钮的位置
                        x = int(960 - (960 - 110) * (now - send_time) / 5)
                        if x <= 110:
                            # 如果按钮已经到达结束位置，隐藏按钮
                            i.hide()
                            delete_list.append(i)
                        else:
                            i.setGeometry(x, 160, 100, 50)
                    for i in delete_list:
                        self.receive_occupied_buttons.remove(i)
                time.sleep(0.05)
            except:
                log(traceback.format_exc())
                break

    def send_pushButton_clicked(self):
        # 获取按钮对应的label的文字——数据包的编号
        packet_num = int(self.sender().text())
        # 从模拟发送流中删除该数据包
        global delay_socket
        delay_socket.delete(packet_num)
        # 解除该按钮的占用
        with self.send_occupied_buttons_lock:
            self.send_occupied_buttons.remove(self.sender())
        # 隐藏该按钮
        self.sender().hide()

    def receive_pushButton_clicked(self):
        # 获取按钮对应的label的文字——数据包的编号
        packet_num = int(self.sender().text())
        # 从模拟发送流中删除该数据包
        global delay_socket
        delay_socket.delete(packet_num)
        # 解除该按钮的占用
        with self.receive_occupied_buttons_lock:
            self.receive_occupied_buttons.remove(self.sender())
        # 隐藏该按钮
        self.sender().hide()

    def send(self, message, address, port):
        global delay_socket
        log("sender send:%s" % message)
        delay_socket.send(address, port, message)

    def sender_send(self, message, now):
        button = self.get_available_button(self.packet_send_buttons, self.send_occupied_buttons)
        button.setText(str(message))
        if button is None:
            # 如果没有空闲的按钮，直接返回
            return
        with self.send_occupied_buttons_lock:
            self.send_occupied_buttons.add(button)
        self.send_button_to_packet[button] = (message, now)
        self.send(message, self.sender_address, self.sender_send_port)
        self.sender_send_queue.do_send(message, now)

    def sender_receive(self):
        receive_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        receive_socket.bind((self.sender_address, self.sender_receive_port))
        receive_socket.listen(1)
        client_socket, client_address = receive_socket.accept()
        message = client_socket.recv(1024).decode()
        log("sender receive:%s" % message)
        receive_socket.close()
        return message

    def receiver_send(self, message):
        button = self.get_available_button(self.packet_receive_buttons, self.receive_occupied_buttons)
        if button is None:
            # 如果没有空闲的按钮，直接返回
            return
        button.setText(str(message))
        with self.receive_occupied_buttons_lock:
            self.receive_occupied_buttons.add(button)
        self.send_button_to_packet[button] = (message, time.time())
        self.send(message, self.receiver_address, self.receiver_send_port)

    def receiver_receive(self):
        receive_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        receive_socket.bind((self.receiver_address, self.receiver_receive_port))
        receive_socket.listen(1)
        client_socket, client_address = receive_socket.accept()
        message = client_socket.recv(1024).decode()
        log("receiver receive:%s" % message)
        receive_socket.close()
        return message

    def run_sender_send_thread(self):
        while True:
            try:
                now = time.time()
                self.sender_send_queue.update(now)
                # 取出下一个要发送的序列号
                seq_num = self.sender_send_queue.get()
                if seq_num is not None:
                    # 发送
                    self.sender_send(seq_num, now)
                time.sleep(1)
            except:
                log(traceback.format_exc())
                break

    def run_sender_receive_thread(self):
        while True:
            try:
                message = self.sender_receive()
                self.sender_send_queue.ack(int(message))
            except:
                log(traceback.format_exc())
                # break

    def run_receiver_receive_thread(self):
        while True:
            try:
                message = self.receiver_receive()
                self.last_received_ack = int(message)
                self.receiver_send(message)
            except:
                log(traceback.format_exc())
                # break

    def start_simulation_pushButton_clicked(self):
        thread_it(self.run_sender_receive_thread)
        thread_it(self.run_receiver_receive_thread)
        time.sleep(0.1)
        thread_it(self.run_sender_send_thread)

        thread_it(self.update_button_position_thread)


if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)
    ui = My_MainWindow()
    ui.show()
    sys.exit(app.exec_())
